//
// Created by edward on 22-11-10.
//

#ifndef CONCURRENCE_STACK_H
#define CONCURRENCE_STACK_H

#include <exception>
#include <memory>
#include <mutex>
#include <stack>
#include <vector>

class EmptyStackException : std::exception {
    const char * what() const noexcept override {
        return "Empty Stack!";
    }
};

namespace edward::multithread {

    /*!
     * 线程安全的栈，底层使用std::stack进行实现
     * @tparam T 栈中元素类型
     * 使用mutex配合简单的lock_guard进行互斥保护，之所以不用COW是因为剩下的接口都是写接口，没有读接口，所以不需要
     */
    template<typename T>
    class Stack {
        using Ptr = std::shared_ptr<T>;
        using LockGuard = std::lock_guard<std::mutex>;

    public:
        //接口相对于std::stack简化了许多，简化的接口让我们得以确保互斥能针对完整操作而锁定
        Stack() = default;
        Stack(const Stack& rhs) {
            LockGuard lock(rhs.mtx_);
            data_ = rhs.data_;
        }
        Stack& operator= (const Stack&) = delete;   //将赋值运算符删除  TODO:?

        //有在考虑要不要让栈中保存shared_ptr，想了想没什么不同了
        void push(T value) {
            LockGuard lock(mtx_);
            data_.push(std::move(value));
        }

        Ptr pop() {
            LockGuard lock(mtx_);
            if (data_.empty()) throw EmptyStackException();

            //觉得要是元素支持移动还是不要这么做，好丑
            Ptr const ret{std::make_shared<T>(data_.top())};    //先拷贝一份，然后再返回其指针，避免再次拷贝出现的异常导致数据丢失。

            data_.pop();
            return ret;
        }
        bool empty() const {
            LockGuard lock(mtx_);
            return data_.empty();
        }

    private:
        std::stack<T, std::vector<T>> data_;    //stack底层使用vector实现，默认是deque
        mutable std::mutex mtx_;    //必须是mutable的，用于保护const Stack的线程安全
    };

}


#endif //CONCURRENCE_STACK_H
